<?php

/**
 * @link      http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license   http://www.yiiframework.com/license/
 */

namespace zkc\gii\generators\crud;

use backend\utils\DBHelper;
use Yii;
use yii\db\BaseActiveRecord;
use yii\gii\CodeFile;
use yii\helpers\StringHelper;
use yii\web\Controller;

/**
 * Generates CRUD
 *
 * @property string $controllerClass The controller class to be generated. This property is
 * read-only.
 *
 * @author Febri M Wicaksono <febrimaschut@gmail.com>
 */
class Generator extends \yii\gii\generators\crud\Generator {
	public $controllerID;
	public $moduleID;

	/**
	 * @inheritdoc
	 */
	public function getName() {
		return 'EasyUi CRUD Generator';
	}

	/**
	 * @inheritdoc
	 */
	public function getDescription() {
		return 'This generator generates a controller and views that implement EasyUi CRUD (Create, Read, Update, Delete)
            operations for the specified data model.';
	}

	/**
	 * @inheritdoc
	 */
	public function rules() {
		return array_merge(\yii\gii\Generator::rules(), [
			[['moduleID', 'controllerID', 'modelClass', 'searchModelClass', 'baseControllerClass'], 'filter', 'filter' => 'trim'],
			[['modelClass', 'controllerID', 'baseControllerClass', 'indexWidgetType'], 'required'],
			[['searchModelClass'], 'compare', 'compareAttribute' => 'modelClass', 'operator' => '!==', 'message' => 'Search Model Class must not be equal to Model Class.'],
			[['modelClass', 'baseControllerClass', 'searchModelClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'],
			[['modelClass'], 'validateClass', 'params' => ['extends' => BaseActiveRecord::className()]],
			[['baseControllerClass'], 'validateClass', 'params' => ['extends' => Controller::className()]],
			[['controllerID'], 'match', 'pattern' => '/^[a-z][a-z0-9\\-\\/]*$/', 'message' => 'Only a-z, 0-9, dashes (-) and slashes (/) are allowed.'],
			[['searchModelClass'], 'validateNewClass'],
			[['controllerClass'], 'filter', 'filter' => function () {
				return $this->getControllerClass();
			}, 'skipOnEmpty'                         => false],
			[['indexWidgetType'], 'in', 'range' => ['grid', 'list']],
			[['modelClass'], 'validateModelClass'],
			[['moduleID'], 'validateModuleID'],
			[['enableI18N'], 'boolean'],
			[['messageCategory'], 'validateMessageCategory', 'skipOnEmpty' => false],
		]);
	}

	/**
	 * @inheritdoc
	 */
	public function attributeLabels() {
		return array_merge(parent::attributeLabels(), [
			'controllerID' => 'Controller ID',
			'moduleID'     => 'Module ID',
		]);
	}

	/**
	 * @inheritdoc
	 */
	public function hints() {
		return array_merge(parent::hints(), [
			'controllerID' => 'Controller ID should be in lower case and may contain module ID(s) separated by slashes. For example:
                <ul>
                    <li><code>order</code> generates <code>OrderController.php</code></li>
                    <li><code>order-item</code> generates <code>OrderItemController.php</code></li>
                    <li><code>admin/user</code> generates <code>UserController.php</code> under <code>admin</code> directory.</li>
                </ul>',
		]);
	}

	/**
	 * Checks if model ID is valid
	 */
	public function validateModuleID() {
		if (!empty($this->moduleID)) {
			$module = Yii::$app->getModule($this->moduleID);
			if ($module === null) {
				$this->addError('moduleID', "Module '{$this->moduleID}' does not exist.");
			}
		}
	}

	/**
	 * @inheritdoc
	 */
	public function generate() {
		$controllerFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->getControllerClass(), '\\')) . '.php');

		$table_name = DBHelper::getRawTableName(call_user_func([$this->modelClass, 'tableName']));

		$table_comment = DBHelper::getTableComment($table_name);

		if (empty($table_comment)) {
			$table_comment = StringHelper::basename($this->modelClass);
		}

		$params                  = [];
		$params['table_comment'] = $table_comment;

		$files = [
			new CodeFile($controllerFile, $this->render('controller.php', $params)),
		];

		if (!empty($this->searchModelClass)) {
			$searchModel = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->searchModelClass, '\\') . '.php'));
			$files[]     = new CodeFile($searchModel, $this->render('search.php', $params));
		}

		$viewPath     = $this->getViewPath();
		$templatePath = $this->getTemplatePath() . '/views';
		foreach (scandir($templatePath) as $file) {
			if (empty($this->searchModelClass) && $file === '_search.php') {
				continue;
			}
			if (is_file($templatePath . '/' . $file) && pathinfo($file, PATHINFO_EXTENSION) === 'php') {
				$files[] = new CodeFile("$viewPath/$file", $this->render("views/$file", $params));
			}
			if ($file == 'js') {
				if (pathinfo($file . '/script.js', PATHINFO_EXTENSION) === 'js') {
					foreach (scandir($templatePath . '/js/') as $jsFile) {
						if (pathinfo($templatePath . '/js/' . $jsFile, PATHINFO_EXTENSION) === 'js') {
							$files[] = new CodeFile("$viewPath/js/$jsFile", $this->render("views/js/$jsFile", $params));
						}
					}
				}
			}
		}

		return $files;
	}

	/**
	 * @return string the action view file path
	 */
	public function getViewPath() {
		$module_id = '';
		$module    = $this->getModule($module_id);


		return $module->getViewPath() . '/' . $this->getControllerPath($module_id);
	}

	/**
	 * @inheritdoc
	 */
	public function successMessage() {
		$route = '/' . $this->controllerID . '/index';
		if (!empty($this->moduleID)) {
			$route = '/' . $this->moduleID . $route;
		}
		$link = \yii\helpers\Html::a('try it now', [$route], ['target' => '_blank']);

		return "The controller has been generated successfully. You may $link.";
	}

	private function getModule(&$module_id = null) {
		$module_id = '';
		$module    = false;

		$mmm = explode('/', $this->controllerID);
		if (count($mmm) > 1) {
			$module_obj = Yii::$app->getModule($mmm[0]);
			if ($module_obj) {
				$module_id = $mmm[0];
				$module    = $module_obj;
			}
		}
		if (!$module) {
			if (!empty($this->moduleID)) {
				$module_id = $this->moduleID;
				$module    = Yii::$app->getModule($this->moduleID);
			} else {
				$module_id = Yii::$app->id;
				$module    = Yii::$app;
			}
		}

		return $module;
	}

	private function getControllerPath($module_id = '') {
		if ($module_id) {
			$controller_path = explode('/', $this->controllerID);
			if (count($controller_path) > 1) {
				if ($controller_path[0] == $module_id) {
					unset($controller_path[0]);

					return implode('/', $controller_path);
				}
			}
		}

		return $this->controllerID;
	}

	/**
	 * @return string the controller class
	 */
	public function getControllerClass() {
		//$module = empty($this->moduleID) ? Yii::$app : Yii::$app->getModule($this->moduleID);
		//$id     = $this->controllerID;

		$module_id = '';
		$module    = $this->getModule($module_id);

		$id = $this->getControllerPath($module_id);

		$pos = strrpos($id, '/');
		if ($pos === false) {
			$prefix    = '';
			$className = $id;
		} else {
			$prefix    = substr($id, 0, $pos + 1);
			$className = substr($id, $pos + 1);
		}
		$className = str_replace(' ', '', ucwords(str_replace('-', ' ', $className))) . 'Controller';
		$className = ltrim($module->controllerNamespace . '\\' . str_replace('/', '\\', $prefix) . $className, '\\');

		return $className;
	}

	// override
	public function getColumnLabel($attribute) {
		/* @var $class ActiveRecord */
		$class = $this->modelClass;
		$label = '';
		if (is_subclass_of($class, 'yii\db\ActiveRecord')) {
			$label = $class::getTableSchema()->getColumn($attribute)->comment;
		} else {
			/* @var $model \yii\base\Model */
			$model = new $class();

			$label = $model->getAttributeLabel($attribute);
		}

		if (empty($label)) {
			$label = $attribute;
		}

		return $label;
	}
}
